/*
 * Copyright [2022] [https://www.xiaonuo.vip]
 *
 * Snowy采用APACHE LICENSE 2.0开源协议，您在使用过程中，需要注意以下几点：
 *
 * 1.请不要删除和修改根目录下的LICENSE文件。
 * 2.请不要删除和修改Snowy源码头部的版权声明。
 * 3.本项目代码可免费商业使用，商业使用请保留源码和相关描述文件的项目出处，作者声明等。
 * 4.分发源码时候，请注明软件出处 https://www.xiaonuo.vip
 * 5.不可二次分发开源参与同类竞品，如有想法可联系团队xiaonuobase@qq.com商议合作。
 * 6.若您的项目无法满足以上几点，需要更多功能代码，获取Snowy商业授权许可，请在官网购买授权，地址为 https://www.xiaonuo.vip
 */
package cn.lixunims.ims.modular.inventory.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.lixunims.ims.modular.basics.entity.*;
import cn.lixunims.ims.modular.basics.service.impl.ImsProductServiceImpl;
import cn.lixunims.ims.modular.basics.service.impl.ImsProductTypeServiceImpl;
import cn.lixunims.ims.modular.basics.service.impl.ImsWarehouseServiceImpl;
import cn.lixunims.ims.modular.bill.entity.ImsBillDetail;
import cn.lixunims.ims.modular.bill.entity.ImsBillHeader;
import cn.lixunims.ims.modular.bill.param.ImsBillAddParam;
import cn.lixunims.ims.modular.inventory.entity.ImsInventoryChangeLog;
import cn.lixunims.ims.modular.inventory.entity.ImsInventoryCheckDetail;
import cn.lixunims.ims.modular.inventory.entity.ImsRealTimeInventory;
import cn.lixunims.ims.modular.inventory.service.ImsInventoryCheckDetailService;
import cn.lixunims.sys.api.SysUserApi;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import cn.lixunims.common.enums.CommonSortOrderEnum;
import cn.lixunims.common.exception.CommonException;
import cn.lixunims.common.page.CommonPageRequest;
import cn.lixunims.ims.modular.inventory.entity.ImsInventoryCheck;
import cn.lixunims.ims.modular.inventory.mapper.ImsInventoryCheckMapper;
import cn.lixunims.ims.modular.inventory.param.ImsInventoryCheckAddParam;
import cn.lixunims.ims.modular.inventory.param.ImsInventoryCheckEditParam;
import cn.lixunims.ims.modular.inventory.param.ImsInventoryCheckIdParam;
import cn.lixunims.ims.modular.inventory.param.ImsInventoryCheckPageParam;
import cn.lixunims.ims.modular.inventory.service.ImsInventoryCheckService;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.ZoneId;
import java.util.*;

/**
 * 库存盘点表Service接口实现类
 *
 * @author YouDong
 * @date  2024/10/21 12:32
 **/
@Service
public class ImsInventoryCheckServiceImpl extends ServiceImpl<ImsInventoryCheckMapper, ImsInventoryCheck> implements ImsInventoryCheckService {

    private final ImsWarehouseServiceImpl imsWarehouseServiceImpl;
    private final ImsProductTypeServiceImpl imsProductTypeServiceImpl;
    @Autowired
    SysUserApi sysUserApi;
    @Autowired
    private ImsRealTimeInventoryServiceImpl imsRealTimeInventoryServiceImpl;
    @Autowired
    private ImsProductServiceImpl imsProductServiceImpl;
    @Autowired
    private ImsInventoryCheckDetailService inventoryCheckDetailService;
    @Autowired
    private ImsInventoryChangeLogServiceImpl imsInventoryChangeLogServiceImpl;
    @Autowired
    private ImsInventoryCheckDetailServiceImpl imsInventoryCheckDetailServiceImpl;

    public ImsInventoryCheckServiceImpl(ImsWarehouseServiceImpl imsWarehouseServiceImpl, ImsProductTypeServiceImpl imsProductTypeServiceImpl) {
        this.imsWarehouseServiceImpl = imsWarehouseServiceImpl;
        this.imsProductTypeServiceImpl = imsProductTypeServiceImpl;
    }

    @Override
    public Page<ImsInventoryCheck> page(ImsInventoryCheckPageParam imsInventoryCheckPageParam) {
        QueryWrapper<ImsInventoryCheck> queryWrapper = new QueryWrapper<ImsInventoryCheck>().checkSqlInjection();
        if(ObjectUtil.isNotEmpty(imsInventoryCheckPageParam.getBillNo())) {
            queryWrapper.lambda().eq(ImsInventoryCheck::getBillNo, imsInventoryCheckPageParam.getBillNo());
        }
        if(ObjectUtil.isAllNotEmpty(imsInventoryCheckPageParam.getSortField(), imsInventoryCheckPageParam.getSortOrder())) {
            CommonSortOrderEnum.validate(imsInventoryCheckPageParam.getSortOrder());
            queryWrapper.orderBy(true, imsInventoryCheckPageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()),
                    StrUtil.toUnderlineCase(imsInventoryCheckPageParam.getSortField()));
        } else {
            queryWrapper.lambda().orderByAsc(ImsInventoryCheck::getId);
        }

        Page<ImsInventoryCheck> page = this.page(CommonPageRequest.defaultPage(), queryWrapper);
        for (ImsInventoryCheck record : page.getRecords()) {
            ImsWarehouse byId = imsWarehouseServiceImpl.getById(record.getWareId());
            ImsProductType productType = imsProductTypeServiceImpl.getById(record.getProductTypeId());
            // 盘点人
            JSONObject user = sysUserApi.getUserByIdWithoutException(record.getCheckerUser());
            record.setUser(user);
            // 分类
            record.setProductType(productType);
            // 仓库
            record.setWarehouse(byId);
        }

        return page;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void add(ImsInventoryCheckAddParam imsInventoryCheckAddParam) {
        ImsInventoryCheck imsInventoryCheck = BeanUtil.toBean(imsInventoryCheckAddParam, ImsInventoryCheck.class);
        this.save(imsInventoryCheck);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void edit(ImsInventoryCheckEditParam imsInventoryCheckEditParam) {
        ImsInventoryCheck imsInventoryCheck = this.queryEntity(imsInventoryCheckEditParam.getId());
        BeanUtil.copyProperties(imsInventoryCheckEditParam, imsInventoryCheck);
        this.updateById(imsInventoryCheck);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(List<ImsInventoryCheckIdParam> imsInventoryCheckIdParamList) {
        // 执行删除
        this.removeByIds(CollStreamUtil.toList(imsInventoryCheckIdParamList, ImsInventoryCheckIdParam::getBillId));
    }

    @Override
    public Map<String, Object> detail(ImsInventoryCheckIdParam imsInventoryCheckIdParam) {

        Map<String, Object> result = new HashMap<>();


        ImsInventoryCheck header = this.queryEntity(imsInventoryCheckIdParam.getBillId());
        // 业务员
        if (StrUtil.isNotBlank(header.getCheckerUser())) {
            try {
                JSONObject userByIdWithoutException = sysUserApi.getUserByIdWithoutException(header.getCheckerUser());
                header.setUser(userByIdWithoutException);
            } catch (Exception ignored) {
            }
        }
        // 仓库
        if (StrUtil.isNotBlank(header.getWareId())) {
            ImsWarehouse warehouse = imsWarehouseServiceImpl.queryEntity(header.getWareId());
            header.setWarehouse(warehouse);
        }

        // 清单
        List<ImsInventoryCheckDetail> list = imsInventoryCheckDetailServiceImpl.list(new LambdaQueryWrapper<ImsInventoryCheckDetail>().eq(ImsInventoryCheckDetail::getBillNo, header.getBillNo()));

        // 商品名称
        for (ImsInventoryCheckDetail imsBillDetail : list) {
            ImsProduct imsProduct = imsProductServiceImpl.queryEntity(imsBillDetail.getProductId());
            if (imsProduct != null) {
                imsBillDetail.setProductTotalName(imsProduct.getProductCode() + "-"+imsProduct.getProductName());
                imsBillDetail.setProduct(imsProduct);
            }
            if (StrUtil.isNotBlank(imsBillDetail.getWareId())) {
                ImsWarehouse byId = imsWarehouseServiceImpl.getById(imsBillDetail.getWareId());
                imsBillDetail.setWarehouse(byId);
            }
        }


        result.put("recordList", list);
        result.put("header", header);

        return result;

    }

    @Override
    public ImsInventoryCheck queryEntity(String id) {
        ImsInventoryCheck imsInventoryCheck = this.getById(id);
        if(ObjectUtil.isEmpty(imsInventoryCheck)) {
            throw new CommonException("库存盘点表不存在，id值为：{}", id);
        }
        return imsInventoryCheck;
    }


    /**
     * 根据仓库或商品类型获取盘点范围
     * @author YouDong
     * @date 2024/10/21
     */
    @Override
    public List<ImsRealTimeInventory> getProductList(String wareId, String productTypeId) {
//        LambdaQueryChainWrapper<ImsProductType> lambdaQuery = imsProductTypeServiceImpl.lambdaQuery();
        LambdaQueryChainWrapper<ImsRealTimeInventory> inentoryLamQuery = imsRealTimeInventoryServiceImpl.lambdaQuery();

        // 查询这个分类或者子分类
        if (StrUtil.isNotBlank(wareId) && !wareId.equals("0")) {
            List<String> wareIdIds = getAllChildTypeIdsWare(wareId);
            wareIdIds.add(wareId); // 包括当前分类
            inentoryLamQuery.in(ImsRealTimeInventory::getWareId, wareIdIds);
        }

        List<ImsRealTimeInventory> list = inentoryLamQuery.list();

        // 过滤商品分类
        if (!list.isEmpty() && StrUtil.isNotBlank(productTypeId) && !productTypeId.equals("0")) {
            List<String> productTypeIdIds = getAllChildTypeIdsType(productTypeId);
            productTypeIdIds.add(productTypeId); // 包括当前分类
            list.forEach(item -> {
                String productId = item.getProductId();
                ImsProduct product = imsProductServiceImpl.getById(productId);
                item.setProduct(product);
            });
            list.removeIf(item -> !productTypeIdIds.contains(item.getProduct().getProductType()));
        }


        return list;
    }


    /**
     * 库存盘点
     * @author YouDong
     * @date 2024/10/21
     */
    @Override
    @Transactional
    public void inventoryCheck(ImsInventoryCheckAddParam imsInventoryCheckAddParam) {


        ImsInventoryCheck header = imsInventoryCheckAddParam.getHeader();
        this.save(header);


        List<ImsRealTimeInventory> recordList = imsInventoryCheckAddParam.getRecordList();
        List<ImsInventoryChangeLog> logs = new ArrayList<>();
        for (ImsRealTimeInventory imsRealTimeInventory : recordList) {
            ImsRealTimeInventory currentInventory = imsRealTimeInventoryServiceImpl.getById(imsRealTimeInventory.getId());


            // 商品
            String productId = imsRealTimeInventory.getProductId();
            ImsProduct product = imsProductServiceImpl.getById(productId);


            // 当前库存数量和总成本
            long currentCount = currentInventory.getCount(); // 当前库存数量
            BigDecimal currentTotalCost = currentInventory.getCost().multiply(new BigDecimal(currentCount)); // 当前库存总成本

            // 盘点数量
            long checkedCount = imsRealTimeInventory.getCount(); // 盘点后的实际数量
            long countDifference = checkedCount - currentCount; // 盘点前后数量差

            // 更新库存数量
            currentInventory.setCount(checkedCount);

            // 根据盘亏或盘盈更新库存成本
            if (countDifference < 0) {
                // 盘亏
                // 计算新的库存成本
                long newCount = currentCount + countDifference; // 新的库存数量

                // 计算新的平均成本
                if (newCount > 0) {
                    BigDecimal averageCost = currentTotalCost.divide(new BigDecimal(newCount), 2, RoundingMode.HALF_UP);
                    currentInventory.setCost(averageCost);
                } else {
                    currentInventory.setCost(BigDecimal.ZERO);
                }
            } else {
                // 盘盈
                // 在盘盈的情况下，新的库存成本通常是现有成本的加权平均
                long newCount = currentCount + countDifference; // 新的库存数量

                // 计算新的平均成本
                if (newCount > 0) {
                    BigDecimal averageCost = currentTotalCost.add(imsRealTimeInventory.getCost().multiply(new BigDecimal(countDifference)))
                            .divide(new BigDecimal(newCount), 2, RoundingMode.HALF_UP);
                    currentInventory.setCost(averageCost);
                } else {
                    // 库存为0
                    currentInventory.setCost(BigDecimal.ZERO);
                }
            }




            // 库存盘点单据
            ImsInventoryCheckDetail imsInventoryCheckDetail = new ImsInventoryCheckDetail();
            imsInventoryCheckDetail.setBillNo(header.getBillNo());
            imsInventoryCheckDetail.setBillId(header.getId());
            imsInventoryCheckDetail.setBillType(header.getBillType());
            imsInventoryCheckDetail.setWareId(imsRealTimeInventory.getWareId());
            imsInventoryCheckDetail.setProductId(imsRealTimeInventory.getProductId());
            imsInventoryCheckDetail.setSupplierId(imsRealTimeInventory.getSupplierId());
            imsInventoryCheckDetail.setBookCount(imsRealTimeInventory.getBookCount());
            imsInventoryCheckDetail.setCount(imsRealTimeInventory.getCount());
            imsInventoryCheckDetail.setSmallCount(imsRealTimeInventory.getSmallCount());
            imsInventoryCheckDetail.setBigCount(imsRealTimeInventory.getBigCount());
            imsInventoryCheckDetail.setRemark(imsRealTimeInventory.getRemark());
            inventoryCheckDetailService.save(imsInventoryCheckDetail);

            // 库存日志
            ImsInventoryChangeLog imsInventoryChangeLog = new ImsInventoryChangeLog();
            imsInventoryChangeLog.setWareId(imsRealTimeInventory.getWareId());
            imsInventoryChangeLog.setProductId(imsRealTimeInventory.getProductId());
            if (imsRealTimeInventory.getBigCount() > 0) {
                imsInventoryChangeLog.setCount(imsRealTimeInventory.getBigCount());
            } else {
                imsInventoryChangeLog.setCount(imsRealTimeInventory.getSmallCount());
            }
            imsInventoryChangeLog.setCount(imsRealTimeInventory.getCount());
            imsInventoryChangeLog.setBillDetailId(imsInventoryCheckDetail.getId());
            imsInventoryChangeLog.setBillId(header.getId());
            // TODO 不对 这个金额不对，不好处理
            imsInventoryChangeLog.setMoney(imsRealTimeInventory.getCost().multiply(new BigDecimal(imsRealTimeInventory.getCount())));
            imsInventoryChangeLog.setUnivalence(imsRealTimeInventory.getCost());
            imsInventoryChangeLog.setOperationType("库存盘点");
            imsInventoryChangeLog.setOperationTime(header.getBillDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
            imsInventoryChangeLog.setOldCount(imsRealTimeInventory.getBookCount());
            imsInventoryChangeLog.setActualCount(imsRealTimeInventory.getCount());
            logs.add(imsInventoryChangeLog);
        }
        // 修改实时库存
        imsRealTimeInventoryServiceImpl.updateBatchById(recordList);
        // 添加库存日志
        imsInventoryChangeLogServiceImpl.saveBatch(logs);


        // TODO 从新处理单据数据


    }

    /**
     * 获取某个分类的所有子分类 ID
     * @param wareId
     * @return
     */
    private List<String> getAllChildTypeIdsWare(String wareId) {
        List<String> childIds = new ArrayList<>();
        // 获取直接子分类
        List<ImsWarehouse> childTypes = imsWarehouseServiceImpl.list(new QueryWrapper<ImsWarehouse>()
                .lambda().eq(ImsWarehouse::getParentId, wareId));

        for (ImsWarehouse childType : childTypes) {
            String childId = childType.getWareId();
            childIds.add(childId);
            // 递归获取子分类的子分类
            List<String> grandChildIds = getAllChildTypeIdsWare(childId);
            childIds.addAll(grandChildIds);
        }

        return childIds;
    }
    private List<String> getAllChildTypeIdsType(String productTypeId) {
        List<String> childIds = new ArrayList<>();
        // 获取直接子分类
        List<ImsWarehouse> childTypes = imsWarehouseServiceImpl.list(new QueryWrapper<ImsWarehouse>()
                .lambda().eq(ImsWarehouse::getParentId, productTypeId));

        for (ImsWarehouse childType : childTypes) {
            String childId = childType.getWareId();
            childIds.add(childId);
            // 递归获取子分类的子分类
            List<String> grandChildIds = getAllChildTypeIdsType(childId);
            childIds.addAll(grandChildIds);
        }

        return childIds;
    }
}
